﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace block_explorer
{
    class SpotLight : BaseSceneLight
    {
        private Vector3 mPosition;
        private Vector3 mDirection;
        private float mRange;
        private float mAngle;
        private float mExponent;

        private RenderTarget2D mShadowMap;
        private int mShadowMapWidth;
        private int mShadowMapHeight;
        private float mNear;

        private Camera mCamera;
        private bool mProjectionDirty;
        private bool mViewDirty;

        private float AspectRatio { get { return mShadowMapWidth / (float)mShadowMapHeight; } }

        public Vector3 Position { get { return mPosition; } set { mPosition = value; mViewDirty = true; } }
        public Vector3 Direction { get { return mDirection; } set { mDirection = value; mViewDirty = true; } }
        public float Range { get { return mRange; } set { mRange = value; mProjectionDirty = true; } }
        public float Angle { get { return mAngle; } set { mAngle = value; mProjectionDirty = true; } }
        public float Exponent { get { return mExponent; } set { mExponent = value; } }
        public Camera Camera
        {
            get
            {
                if (mProjectionDirty)
                {
                    UpdateProjectionMatrix();
                    mProjectionDirty = false;
                }
                if (mViewDirty)
                {
                    UpdateViewMatrix();
                    mViewDirty = false;
                }
                return mCamera;
            }
        }
        public override Texture ShadowMap { get { return mShadowMap; } }

        public SpotLight()
        {
            mPosition = Vector3.Zero;
            mDirection = Vector3.One;
            mRange = 1.0f;
            mAngle = 45.0f;
            mExponent = 1.0f;

            mCamera = new Camera();
            mProjectionDirty = true;
            mViewDirty = true;

            mShadowMapWidth = 512;
            mShadowMapHeight = 512;
            mNear = 0.1f;
        }

        public SpotLight(Vector3 position, Vector3 direction, Color color, float range, float angle, float exponent)
            :base(color)
        {
            mPosition = position;
            mDirection = direction;
            mRange = range;
            mAngle = angle;
            mExponent = exponent;

            mCamera = new Camera();
            mProjectionDirty = true;
            mViewDirty = true;

            mShadowMapWidth = 512;
            mShadowMapHeight = 512;
            mNear = 0.1f;
        }

        private void UpdateViewMatrix()
        {
            Vector3 up = Direction == Vector3.Up ? Vector3.Forward : Vector3.Up;
            mCamera.View = Matrix.CreateLookAt(Position, Position + Direction, up);
        }

        private void UpdateProjectionMatrix()
        {
            mCamera.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(Angle * 2.0f), AspectRatio, mNear, Range);
        }

        public override void PrepareShadowMap(GraphicsDevice device)
        {
            mShadowMap = new RenderTarget2D(device, mShadowMapWidth, mShadowMapHeight, false, SurfaceFormat.Single, DepthFormat.Depth24);
        }

        public override void DrawShadowMap(GraphicsDevice device, Shaders.ShadowMapEffect occlusionEffect, Scene scene, SceneCamera camera)
        {
            device.SetRenderTarget(mShadowMap);
            device.Clear(Color.White);
            occlusionEffect.Technique = Shaders.ShadowMapEffect.ShadowTechnique.Spot;
            occlusionEffect.Passes[0].Apply();
            scene.Draw(occlusionEffect, Camera);
        }
    }
}
